home *** CD-ROM | disk | FTP | other *** search
/ CD ROM Paradise Collection 4 / CD ROM Paradise Collection 4 1995 Nov.iso / program / tjgold.zip / INSTALL.002 / DFPAGE.PAS < prev    next >
Pascal/Delphi Source File  |  1995-07-19  |  37KB  |  921 lines

  1. {--------------------------------------------------------------------------}
  2. {                Product: TechnoJock's Turbo Toolkit                       }
  3. {                Version: GOLD                                             }
  4. {                Build:   1.01                                             }
  5. {                                                                          }
  6. {  The index routines used in TTT Gold were developed by Dean Farwell II   }
  7. {  and are an adaptation of his excellent TBTREE database tools.           }
  8. {                                                                          }
  9. {                   Copyright 1988-1994 Dean Farwell II                    }
  10. {        Portions Copyright 1986-1995  TechnoJock Software, Inc.           }
  11. {                           All Rights Reserved                            }
  12. {                          Restricted by License                           }
  13. {--------------------------------------------------------------------------}
  14.  
  15.                      {********************************}
  16.                      {       Unit:   DFPAGE           }
  17.                      {********************************}
  18.  
  19. unit DFPage;
  20.  
  21. {$I-}                                          (* turn on I/O error checking *)
  22.  
  23. (*****************************************************************************)
  24. (*                                                                           *)
  25. (*          P A G E  B U F F E R  H A N D L I N G  R O U T I N E S           *)
  26. (*                                                                           *)
  27. (*****************************************************************************)
  28.  
  29. (*  This unit handles the page buffer.  This buffer is used for keeping
  30.     disk pages in memory.  The pages can be for data files or index files.
  31.     The buffer uses a demand paging scheme in which the least recently used
  32.     page is swapped out when a page is needed and the buffer is full.        *)
  33.  
  34.  
  35. (*////////////////////////// I N T E R F A C E //////////////////////////////*)
  36.  
  37. interface
  38.  
  39. uses
  40.     Dos,
  41.     DFBTreUt;
  42.  
  43. type
  44.  
  45.     BufferSizeType = 0 .. 1024;    (* used for number of pages in the buffer *)
  46.  
  47.  
  48. (* This routine will check to see if a given physical record for a given file
  49.    actually exists either on disk or in the buffer.  It first checks the
  50.    buffer.  If its not in the buffer, it checks to see if it is past the
  51.    end of the file.  It essentially replaces EOF.  EOF will not work properly
  52.    if the pages reside in the buffer but have not been written to disk yet.
  53.  
  54.    Note - This routine is quite different than routines found in the LOGICAL
  55.    unit and the BTREE unit.  Those units use bitmaps to to see if a record is
  56.    actively being used as opposed to existing and containing garbage.
  57.    PageExists only checks the physical existence of a physical record.  It
  58.    does not check bitmaps like the others do.  It first checks the page buffer
  59.    to see if the page exists there.  If it is not found there, then the file
  60.    itself is checked.                                                        *)
  61.  
  62.  
  63. function PageExists(fName : PathStr;
  64.                     var FId : File;                    (* var for speed only *)
  65.                     prNum : PrNumber) : Boolean;
  66.  
  67.  
  68. (* This function will fetch a page and return a copy of the page to the caller.
  69.    It accomplishes this by first looking in the buffer itself.  If it can't
  70.    locate it in the buffer, it checks to see if there is room in the buffer.
  71.    If there is no available room, the least recently used page is written to
  72.    disk.  That frees up that page for use.  It will then read in the page from
  73.    disk.
  74.  
  75.    Note - This routine expects the page physical record to exist somewhere
  76.    (either on the disk or in the page buffer)                                *)
  77.  
  78. procedure FetchPage(fName : PathStr;
  79.                     var fId : File;                    (* var for speed only *)
  80.                     prNum : PrNumber;
  81.                     var pg : SinglePage);
  82.  
  83. (*\*)
  84. (* This routine will store a page in the buffer.  It accomplishes this by
  85.    seeing if an old version is in the buffer.  If it is not it creates a new
  86.    page.  The page is stored, the dirty flag is set, and the timeUsed is
  87.    set.
  88.  
  89.    This can be used to store a page even if the corresponding page does not
  90.    yet exist.  In this case, the record will be created and stored in the
  91.    buffer. It will be physically created in the file when the page is written
  92.    to disk.
  93.  
  94.    note - This routine will immediately write this page to disk if the user
  95.    has called SetImmediateDiskWrite with a value of TRUE.  Using this feature
  96.    will ensure that current info is always on the disk but will greatly reduce
  97.    efficiency.                                                               *)
  98.  
  99. procedure StorePage(fName : PathStr;
  100.                     var fId : File;                    (* var for speed only *)
  101.                     prNum : PrNumber;
  102.                     pg : SinglePage);
  103.  
  104.  
  105. (* This routine will release the page in the buffer for a given physical
  106.    record in a given file.  Of course, the routine first checks to see
  107.    if the record is in fact in the buffer.                                   *)
  108.  
  109. procedure ReleasePage(fName : PathStr;
  110.                       prNum : PrNumber);
  111.  
  112.  
  113. (* This routine will release all pages in the buffer for the given file (fName)
  114.    It is extremely important to realize that this DOES NOT write the buffer
  115.    pages to disk prior to releasing them.  It is intended for internal use.
  116.    You should use ClearBuffer instead in that ClearBuffer will ensure that
  117.    pages are not lost.                                                       *)
  118.  
  119. procedure ReleaseAllPages(fName : PathStr);
  120.  
  121.  
  122. (*\*)
  123. (* This routine will allow the user to set the maximum number of buffer pages
  124.    to be in use at one time.  This routine allows the user to change this
  125.    at ANY time while the program is running.  The program will check to
  126.    ensure that the user is not setting the maximum number of pages in use
  127.    to an illegal value.  An illegal value is zero or less.  The buffer must
  128.    contain at least one page to function properly.  If the caller has
  129.    specified a new setting which is below the number of pages in use, the
  130.    routine will release pages randomly until the count of pages in use is
  131.    reduced to n.  There is nothing fancy about the algorithm to chose pages
  132.    to release.  The user can alleviate having the wrong pages swapped out
  133.    by specifying certain pages to be swapped out prior to calling this.
  134.    For example, the user could save and release all pages for a file which
  135.    won't be used for awhile.  Remember, swapping out the wrong pages will
  136.    not cause errors, but it may temporarily affect performance as the pages
  137.    will have to be read back in upon their next use.  As an aside, I did
  138.    not swap out least recently used pages since a large number might be
  139.    swapped out.  Each swap would entail going through the entire buffer to
  140.    find the least recently used page.  This would cause too much overhead.
  141.  
  142.    note - notice use of Exit for exiting the routine.  The routine will not
  143.    normally fall out the bottom.                                             *)
  144.  
  145. procedure SetMaxBufferPages(n : BufferSizeType);
  146.  
  147.  
  148. (* This routine will print the entire page buffer.  lst is the parameter which
  149.    specifies which text device you want to use for output. Normally, it will
  150.    be the printer.  Be sure that the device is initialized properly using
  151.    Assign and Rewrite prior to calling this routine.                         *)
  152.  
  153. procedure PrintPageBuffer(var lst : PrintTextDevice);
  154.  
  155.  
  156. procedure PrintPageBufferPage(var lst : PrintTextDevice;
  157.                               prNum : PrNumber);
  158.  
  159.  
  160. (* This routine will print the buffer statistics.  lst is the parameter which
  161.    specifies which text device you want to use for output. Normally, it will
  162.    be the printer.  Be sure that the device is initialized properly using
  163.    Assign and Rewrite prior to calling this routine.                         *)
  164.  
  165. procedure PrintBufferStats(var lst : PrintTextDevice);
  166.  
  167. (*!*)
  168. (*\*)
  169. (*///////////////////// I M P L E M E N T A T I O N /////////////////////////*)
  170.  
  171. (* the following declarations are for defining and storing the buffer *)
  172.  
  173. implementation
  174.  
  175.  
  176. const
  177.     POINTERARRAYSIZE = 199;          (* used to set up array of linked lists
  178.                                          this number needs to be prime       *)
  179.  
  180. type
  181.     PagePtr = ^PageEntry;
  182.  
  183.     PageEntry  = record
  184.                  fName        : PathStr;
  185.                  prNum        : PrNumber;
  186.                  timeUsed     : TimeArr;
  187.                  page         : SinglePage;
  188.                  nextPage     : PagePtr;
  189.                  end;
  190.  
  191.     PointerArrayRange = 0 .. POINTERARRAYSIZE;
  192.  
  193.  
  194. var
  195.     pagesInUse : BufferSizeType;     (* value should never exceed the current
  196.                                                      value of maxBufferPages *)
  197.  
  198.     pointerArray : Array [PointerArrayRange] of PagePtr;  (* Type of Array
  199.                                                          holding the pointers
  200.                                                          to the linked list of
  201.                                                          pages in the
  202.                                                          page buffer *)
  203.  
  204.     reservedPgPtr : PagePtr;              (* used to reserve enough room on
  205.                                              the heap for at least one page  *)
  206.  
  207.  
  208. (* the following declarations are for keeping and printing statistics on
  209.    buffer usage                                                              *)
  210.  
  211. type
  212.     StatsRange = 0 .. MAXLONGINT;  (* used as type for many buffer stat vars *)
  213.  
  214.     BufferStats = record                   (* used to hold buffer statistics *)
  215.                   pagesInUse : StatsRange;
  216.                   maxPages : StatsRange;
  217.                   attempts : StatsRange;
  218.                   hits : StatsRange;
  219.                   end;
  220.  
  221.  
  222. var
  223.     maxBufferPages : BufferSizeType;  (* Number of buffer pages in buffer.
  224.                                          This can be set by the user to
  225.                                          allow a flexible buffer size        *)
  226.  
  227.  
  228.     bufferAttempts: StatsRange;     (* total attempts to fetch a page from the
  229.                                        buffer                                *)
  230.  
  231.     bufferHits : StatsRange;        (* used for to keep track of attempts to
  232.                                        fetch a physical record from the buffer
  233.                                        in which the record was there         *)
  234.  
  235. (*\*)
  236. (* This routine will initialize the pointer array to all NILS and will set
  237.    the pages in the pagesInUse counter to zero.  This last item will reflect
  238.    the fact that there are no pages active in the buffer.                    *)
  239.  
  240. procedure InitializePointerArray;
  241.  
  242. var
  243.     cnt : PointerArrayRange;
  244.  
  245.     begin
  246.     for cnt := 0 to POINTERARRAYSIZE do
  247.         begin
  248.         pointerArray[cnt] := NIL;
  249.         end;
  250.     pagesInUse := 0;
  251.     end;                            (* end of InitializePointerArray routine *)
  252.  
  253.  
  254. (* This routine will write a specified page to disk.  It will also change the
  255.    Dirty flag to FALSE showing that the page is not dirty.                   *)
  256.  
  257. procedure WriteToDisk(pgPtr : PagePtr;
  258.                       var fId : File                   (* var for speed only *)
  259.                       );
  260. var
  261.   errorCode : IOErrorCode;
  262. begin
  263.    {$I-}Seek(fId,pgPtr^.prNum);{$I+}
  264.    errorCode := IOresult;
  265.    if errorCode <> 0 then
  266.    begin
  267.       SetBtreeError(errorCode);
  268.       exit;
  269.    end;
  270.    {$I-}BlockWrite(fId,pgPtr^.page,1);{$I+}
  271.    errorCode := IOresult;
  272.    if errorCode <> 0 then
  273.       SetBtreeError(errorCode);
  274. end;           (* end of WriteToDisk procedure *)
  275.  
  276. (*\*)
  277. (* This routine will read in a specified page from disk.  It will change the
  278.    Dirty flag to false showing that the page is not dirty.  It will also
  279.    set the file name and set the physical record number.  It does not set the
  280.    the time.  This will be done by the procedure which actually decides to
  281.    fetch this record.                                                        *)
  282.  
  283. procedure ReadFromDisk(var fName : PathStr;           (* var for speed only *)
  284.                        var fId : File;                (* var for speed only *)
  285.                        prNum : PrNumber;
  286.                        pgPtr : PagePtr);
  287. var
  288.   errorCode : IOErrorCode;
  289. begin
  290.     {$I-}Seek(fId,prNum);{$I+}
  291.     errorCode := IOresult;
  292.     if errorCode <> 0 then
  293.     begin
  294.        SetBtreeError(errorCode);
  295.        Exit;
  296.     end;
  297.     {$I-}BlockRead(fId,pgPtr^.page,1);{$I+}
  298.     errorCode := IOresult;
  299.     if errorCode <> 0 then
  300.     begin
  301.        SetBtreeError(errorCode);
  302.        Exit;
  303.     end;
  304.     pgPtr^.fName := fName;
  305.     pgPtr^.prNum := prNum;
  306. end;          (* end of ReadFromDisk procedure *)
  307.  
  308.  
  309. (* This routine will return the index to the pointerArray corresponding to the
  310.    given file and physical record.                                           *)
  311.  
  312. function Hash(var fName : PathStr;                    (* var for speed only *)
  313.               prNum : PrNumber) : PointerArrayRange;
  314.  
  315. {$V-}
  316. begin
  317.    Hash := (prNum + TotalString(fName)) Mod POINTERARRAYSIZE;
  318. end;           (* end of Hash routine *)
  319. {$V+}
  320.  
  321. (*\*)
  322. (* This routine will return a pointer pointing to the page corresponding to a
  323.    given file and physical record number.  It will return NIL if the page is
  324.    not in the buffer.                                                        *)
  325.  
  326. function GetPagePtr(var fName : PathStr;              (* var for speed only *)
  327.                     prNum : PrNumber) : PagePtr;
  328.  
  329. var
  330.     tempPtr : PagePtr;
  331.     found : boolean;
  332.  
  333.     begin
  334.     tempPtr := pointerArray[Hash(fName,prNum)];
  335.     found := FALSE;
  336.     while (not found) and (tempPtr <> NIL) do
  337.         begin
  338.         if (tempPtr^.prNum = prNum) and (tempPtr^.fName = fName) then
  339.            begin
  340.            found := TRUE;
  341.            end
  342.        else
  343.            begin
  344.            tempPtr := tempPtr^.nextPage;
  345.            end;
  346.        end;
  347.    GetPagePtr := tempPtr;
  348.    end;                                           (* end of FindPage routine *)
  349.  
  350.  
  351. (* This routine will pull a page out of a page list.  It does not Dispose of
  352.    the page.  This allows the page to be immediately reused.  The calling
  353.    routine should either reuse it or Dispose it.                             *)
  354.  
  355. procedure DeletePgFromList(pgPtr : PagePtr);
  356.  
  357. var
  358.     tempPtr : PagePtr;
  359.  
  360.     begin
  361.     tempPtr := pointerArray[Hash(pgPtr^.fName,pgPtr^.prNum)];
  362.     if tempPtr = pgPtr then
  363.         begin                             (* page to delete is first in list *)
  364.         pointerArray[Hash(pgPtr^.fName,pgPtr^.prNum)] := pgPtr^.nextPage;
  365.         end
  366.     else
  367.         begin
  368.         while tempPtr^.nextPage <> pgPtr do
  369.             begin
  370.             tempPtr := tempPtr^.nextPage;
  371.             end;
  372.         tempPtr^.nextPage := pgPtr^.nextPage;
  373.         end;
  374.     end;                                  (* end of DeletePgFromList routine *)
  375.  
  376. (*\*)
  377. (* This routine will take a page and insert it into the proper place in the
  378.    buffer.                                                                   *)
  379.  
  380. procedure InsertPgInList(var fName : PathStr;         (* var for speed only *)
  381.                          prNum : PrNumber;
  382.                          pgPtr : PagePtr);
  383.  
  384. var
  385.     arrayIndex : PointerArrayRange;
  386.  
  387.     begin
  388.     arrayIndex := Hash(fName,prNum);
  389.     pgPtr^.nextPage := pointerArray[arrayIndex];  (* insert page as first    *)
  390.     pointerArray[arrayIndex] := pgPtr;            (* page in page list       *)
  391.     end;                                    (* end of InsertPgInList routine *)
  392.  
  393.  
  394. (* This routine creates a new page and inserts the new page in the front of
  395.    the appropriate page list.  It does not set any of the fields in the prPtr^
  396.    record (except for the nextPage pointer).  This routine does not check to
  397.    see if there is a page available.   This is the responsibility of the
  398.    caller.                                                                   *)
  399.  
  400. procedure CreateNewPage(var fName : PathStr;          (* var for speed only *)
  401.                         prNum : PrNumber;
  402.                         var pgPtr : PagePtr);
  403.  
  404.     begin
  405.     New(pgPtr);
  406.     Inc(pagesInUse);                                (* one more page used up *)
  407.     InsertPgInList(fName,prNum,pgPtr);               (* put page into proper
  408.                                                              place in buffer *)
  409.     end;                                     (* end of CreateNewPage routine *)
  410.  
  411. (*\*)
  412. (* This routine will find the least recently used page, delete it from the
  413.    page list and write it to disk (if it is dirty).  The pointer to the page
  414.    is then returned                                                          *)
  415.  
  416. function LRUPage : PagePtr;
  417.  
  418. var
  419.     cnt : PointerArrayRange;
  420.     tempPgPtr,
  421.     leastPgPtr : PagePtr;
  422.     minTime : TimeArr;
  423.  
  424.     begin
  425.     SetMaxTime(minTime);
  426.     leastPgPtr := NIL;
  427.     for cnt := 0 to POINTERARRAYSIZE do
  428.         begin
  429.         tempPgPtr := pointerArray[cnt];
  430.         while tempPgPtr <> NIL do
  431.             begin
  432.             if CompareTime(tempPgPtr^.timeUsed,mintime) = LESSTHAN then
  433.                 begin
  434.                 minTime := tempPgPtr^.timeUsed;
  435.                 leastPgPtr := tempPgPtr;
  436.                 end;
  437.             tempPgPtr := tempPgPtr^.nextPage;
  438.             end;
  439.         end;
  440.     DeletePgFromList(leastPgPtr);              (* pull page out of page list *)
  441.     LRUPage := leastPgPtr;               (* return pointer to page to caller *)
  442.     end;                                           (* end of LRUPage routine *)
  443.  
  444. (*\*)
  445. (* This routine will check to see if a given physical record for a given file
  446.    actually exists either on disk or in the buffer.  It first checks the
  447.    buffer.  If its not in the buffer, it checks to see if it is past the
  448.    end of the file.  It essentially replaces EOF.  EOF will not work properly
  449.    if the pages reside in the buffer but have not been written to disk yet.
  450.  
  451.    Note - This routine is quite different than routines found in the LOGICAL
  452.    unit and the BTREE unit.  Those units use bitmaps to to see if a record is
  453.    actively being used as opposed to existing and containing garbage.
  454.    PageExists only checks the physical existence of a physical record.  It
  455.    does not check bitmaps like the others do.  It first checks the page buffer
  456.    to see if the page exists there.  If it is not found there, then the file
  457.    itself is checked.                                                        *)
  458.  
  459. function PageExists(fName : PathStr;
  460.                     var FId : File;                    (* var for speed only *)
  461.                     prNum : PrNumber) : Boolean;
  462.  
  463. var
  464.     fSize : PrNumber;
  465.     errorCode : IOErrorCode;
  466.  
  467.     begin
  468.     if GetPagePtr(fName,prNum) = NIL then  (* check to see if rec is in buff *)
  469.         begin
  470.         fSize := FileSize(fID);
  471.         if errorCode <> 0 then
  472.             begin
  473.             SetBtreeError(errorCode);
  474.             Exit;
  475.             end;
  476.         if prNum <= FSize - 1 then
  477.             begin                             (* record not past end of file *)
  478.             PageExists := TRUE;
  479.             end
  480.         else
  481.             begin               (* record not in buffer and past end of file *)
  482.             PageExists := FALSE;
  483.             end;
  484.         end
  485.     else
  486.         begin                    (* page is in buffer .. therefore it exists *)
  487.         PageExists := TRUE;
  488.         end;
  489.     end;                                        (* end of PageExists routine *)
  490.  
  491. (*\*)
  492. (* This function will fetch a page and return a copy of the page to the caller.
  493.    It accomplishes this by first looking in the buffer itself.  If it can't
  494.    locate it in the buffer, it checks to see if there is room in the buffer.
  495.    If there is no available room, the least recently used page is written to
  496.    disk.  That frees up that page for use.  It will then read in the page from
  497.    disk.
  498.  
  499.    Note - This routine expects the page physical record to exist somewhere
  500.    (either on the disk or in the page buffer)                                *)
  501.  
  502. procedure FetchPage(fName : PathStr;
  503.                     var fId : File;                    (* var for speed only *)
  504.                     prNum : PrNumber;
  505.                     var pg : SinglePage);
  506.  
  507. var
  508.     pgPtr : PagePtr;
  509.  
  510.     begin
  511.     pgPtr := GetPagePtr(fName,prNum);          (* try to find page in buffer *)
  512.     if pgPtr = NIL then                    (* check to see if page was found *)
  513.         begin                                    (* page not found in buffer *)
  514.         if (pagesInUse < maxBufferPages) and       (* check for unused pages *)
  515.            (MaxAvail >= SizeOf(PageEntry)) then      (* check for heap space *)
  516.             begin                             (* there is room in the buffer *)
  517.             CreateNewPage(fName,prNum,pgPtr);    (* make new page and use it *)
  518.             end
  519.         else
  520.             begin                                         (* no unused pages *)
  521.             if pagesInUse = 0 then
  522.                 begin
  523.                 pgPtr := reservedPgPtr;          (* used reserved heap space *)
  524.                 end
  525.             else
  526.                 begin
  527.                 pgPtr := LRUPage;            (* get least recently used page *)
  528.                                                      (* and write it to disk *)
  529.                 end;
  530.             InsertPgInList(fName,prNum,pgPtr);       (* put page into proper
  531.                                                              place in buffer *)
  532.             end;
  533.         ReadFromDisk(fName,fId,prNum,pgPtr);         (* read in desired page *)
  534.         if BTreeErrorOccurred then Exit;
  535.         end
  536.     else
  537.         begin                                           (* page is in buffer *)
  538.         Inc(bufferHits);                              (* update hits counter *)
  539.         end;
  540.     GetTime(pgPtr^.timeUsed);                 (* set time page was requested *)
  541.     Move(pgPtr^.page,pg,SizeOf(pg));          (* return copy of the actual
  542.                                                  page to the caller       *)
  543.     Inc(bufferAttempts);
  544.     end;                                         (* end of FetchPage routine *)
  545.  
  546. (*\*)
  547. (* This routine will store a page in the buffer.  It accomplishes this by
  548.    seeing if an old version is in the buffer.  If it is not it creates a new
  549.    page.  The page is stored and the timeUsed is set.
  550.  
  551.    This can be used to store a page even if the corresponding page does not yet
  552.    exist.  In this case, the record will be created and stored in the buffer.
  553.    It will be physically created in the file when the page is written to
  554.    disk.
  555.  
  556.    note - This routine will immediately write this page to disk.             *)
  557.  
  558. procedure StorePage(fName : PathStr;
  559.                     var fId : File;                    (* var for speed only *)
  560.                     prNum : PrNumber;
  561.                     pg : SinglePage);
  562.  
  563. var
  564.     pgPtr : PagePtr;
  565.     oldPg : SinglePage;
  566.  
  567.     begin
  568. {$B-}                            (* next statement depends on short circuit
  569.                                               boolean expression evaluation  *)
  570.  
  571.     pgPtr := GetPagePtr(fName,prNum);
  572.     if pgPtr = NIL then
  573.         begin
  574.         if (pagesInUse <> maxBufferPages) and      (* check for unused pages *)
  575.            (MaxAvail >= SizeOf(PageEntry)) then      (* check for heap space *)
  576.             begin
  577.             CreateNewPage(fName,prNum,pgPtr);
  578.             end
  579.         else
  580.             begin
  581.             if pagesInUse = 0 then
  582.                 begin
  583.                 pgPtr := reservedPgPtr;          (* used reserved heap space *)
  584.                 end
  585.             else
  586.                 begin
  587.                 pgPtr := LRUPage;            (* get least recently used page *)
  588.                                                      (* and write it to disk *)
  589.                 end;
  590.             InsertPgInList(fName,prNum,pgPtr);       (* put page into proper
  591.                                                              place in buffer *)
  592.             end;
  593.         pgPtr^.fName := fName;
  594.         pgPtr^.prNum := prNum;
  595.         end;
  596.     Move(pg,pgPtr^.page,SizeOf(pg));    (* move page to store into buffer *)
  597.     GetTime(pgPtr^.timeUsed);
  598.     WriteToDisk(pgPtr,fId);
  599.     if BTreeErrorOccurred then Exit;
  600.     end;                                         (* end of StorePage routine *)
  601.  
  602. (*\*)
  603. (* This routine will release the page in the buffer for a given physical
  604.    record in a given file.  Of course, the routine first checks to see
  605.    if the record is in fact in the buffer.  It is important to realize that
  606.    this page will not be written to disk, but will be lost.                  *)
  607.  
  608. procedure ReleasePage(fName : PathStr;
  609.                       prNum : PrNumber);
  610.  
  611. var
  612.     pgPtr : PagePtr;
  613.  
  614.     begin
  615.     pgPtr := GetPagePtr(fName,prNum);
  616.     if pgPtr <> NIL then
  617.         begin
  618.         DeletePgFromList(pgPtr);
  619.         if pgPtr <> reservedPgPtr then
  620.             begin                (* dispose of the heap space unless it is
  621.                                     the reserved space                       *)
  622.             Dispose(pgPtr);
  623.             end;
  624.         Dec(pagesInUse);
  625.         end;
  626.     end;                                       (* end of ReleasePage routine *)
  627.  
  628.  
  629. (* This routine will release all pages in the buffer for the given file (fName)
  630.    It is extremely important to realize that this DOES NOT write the buffer
  631.    pages to disk prior to releasing them.  It is intended for internal use.
  632.    You should use ClearBuffer instead in that ClearBuffer will ensure that
  633.    pages are not lost.                                                       *)
  634.  
  635. procedure ReleaseAllPages(fName : PathStr);
  636.  
  637. var
  638.     pgPtr : PagePtr;
  639.     cnt : PointerArrayRange;
  640.  
  641.     begin
  642.     for cnt := 0 to POINTERARRAYSIZE do
  643.         begin
  644.         pgPtr := pointerArray[cnt];
  645.         while pgPtr <> NIL do
  646.             begin
  647.             if pgPtr^.fName = fName then
  648.                 begin
  649.                 ReleasePage(fName,pgPtr^.prNum);
  650.                 pgPtr := PointerArray[cnt];     (* reset to a valid location *)
  651.                 end
  652.             else
  653.                 begin
  654.                 pgPtr := pgPtr^.nextPage;
  655.                 end;
  656.             end;
  657.         end;
  658.     end;                                   (* end of ReleaseAllPages routine *)
  659.  
  660.  
  661. (* This routine will allow the user to set the maximum number of buffer pages
  662.    to be in use at one time.  This routine allows the user to change this
  663.    at ANY time while the program is running.  The program will check to
  664.    ensure that the user is not setting the maximum number of pages in use
  665.    to an illegal value.  An illegal value is zero or less.  The buffer must
  666.    contain at least one page to function properly.  If the caller has
  667.    specified a new setting which is below the number of pages in use, the
  668.    routine will release pages randomly until the count of pages in use is
  669.    reduced to n.  There is nothing fancy about the algorithm to chose pages
  670.    to release.  The user can alleviate having the wrong pages swapped out
  671.    by specifying certain pages to be swapped out prior to calling this.
  672.    For example, the user could write to disk and release all pages for a file
  673.    which won't be used for awhile.  Remember, swapping out the wrong pages
  674.    will not cause errors, but it may temporarily affect performance as the
  675.    pages will have to be read back in upon their next use.  As an aside, I did
  676.    not swap out least recently used pages since a large number might be
  677.    swapped out.  Each swap would entail going through the entire buffer to
  678.    find the least recently used page.  This would cause too much overhead.   *)
  679.  
  680. procedure SetMaxBufferPages(n : BufferSizeType);
  681.  
  682. var
  683.     pgPtr : PagePtr;
  684.     cnt : PointerArrayRange;
  685.  
  686.     begin
  687.     if n > 0 then      (* make sure that value is not 0! if it is do nothing *)
  688.         begin
  689.         cnt := 0;
  690.         while pagesInUse > n do
  691.             begin           (* if more pages are in use than desired, release
  692.                                them until the desired number    is reached   *)
  693.             pgPtr := pointerArray[cnt];                    (* reset pgPtr to
  694.                                                             a valid location *)
  695.             if pgPtr <> NIL then
  696.                 begin
  697.                 ReleasePage(pgPtr^.fName,pgPtr^.prNum);
  698.                 end
  699.             else
  700.                 begin
  701.                 Inc(cnt);
  702.                 end;
  703.             end;
  704.         maxBufferPages := n;
  705.         end;
  706.     end;                                 (* end of SetMaxBufferPages routine *)
  707.  
  708. (*\*)
  709. (* These routines support debugging of the page buffer routines              *)
  710.  
  711. procedure PrintPageInfo(var lst : PrintTextDevice;
  712.                         pgPtr : PagePtr);
  713.  
  714.     (* Prints out string equivalent of boolean value *)
  715.     procedure PrintBoolean(x : boolean);
  716.  
  717.     begin
  718.     case x of
  719.         FALSE : Write(lst,'FALSE');
  720.         TRUE  : Write(lst,'TRUE');
  721.         end;                                        (* end of case statement *)
  722.     end;                                   (* end of PrintPageBuffer routine *)
  723.  
  724.     (* determines if x is a screen printable non control character *)
  725.     function PrintableChar(x : Char) : boolean;
  726.  
  727.     begin
  728.     PrintableChar := Integer(x) in [32 .. 127];
  729.     end;                                     (* end of PrintableChar routine *)
  730.  
  731. const
  732.     LINESIZE = 24;          (* number of bytes output on one line of printer *)
  733.  
  734. var
  735.     loopByteCnt,            (* used in inner loop to point to bytes *)
  736.     maxLoopByteCnt,         (* used in inner loop to keep from going past
  737.                                end of buffer page  *)
  738.     byteCnt : PageRange;    (* current byte in buffer page *)
  739.     done : boolean;         (* used for inner loop termination *)
  740.  
  741.     begin
  742.     Writeln(lst,'     fName = ',pgPtr^.fName);
  743.     Writeln(lst,'     prNum = ',pgPtr^.prNum);
  744.     Writeln(lst);
  745.     Write(lst,'     timeUsed = ');
  746.     Write(lst,pgPtr^.timeUsed.msLongInt,'     ');
  747.     Write(lst,pgPtr^.timeUsed.lsLongInt);
  748.     Writeln(lst); Writeln(lst);
  749.     byteCnt := 1;
  750.     done := FALSE;
  751.     repeat
  752.         begin
  753.         if ((byteCnt + LINESIZE) - 1) <= PAGESIZE then
  754.             begin
  755.             maxLoopByteCnt := byteCnt + LINESIZE - 1;
  756.             end
  757.         else
  758.             begin
  759.             maxLoopByteCnt := PAGESIZE;
  760.             end;
  761.         (* print column position *)
  762.         for loopByteCnt := byteCnt to maxLoopByteCnt do
  763.             begin
  764.             Write(lst,loopByteCnt : 3,' ');
  765.             end;
  766.         Writeln(lst);
  767.         (* Print HEX value *)
  768.         for loopByteCnt := byteCnt to maxLoopByteCnt do
  769.             begin
  770.             Write(lst,'$',ByteToHex(pgPtr^.page[loopByteCnt]),' ');
  771.             end;
  772.         Writeln(lst);
  773.         (* print integer equivalent *)
  774.         for loopByteCnt := byteCnt to maxLoopByteCnt do
  775.             begin
  776.             Write(lst,pgPtr^.page[loopByteCnt] :3,' ');
  777.             end;
  778.         Writeln(lst);
  779.         (* character equivalent or print '*' if char not printable *)
  780.         for loopByteCnt := byteCnt to maxLoopByteCnt do
  781.             begin
  782.             if PrintableChar(Chr(pgPtr^.page[loopByteCnt])) then
  783.                 begin
  784.                 Write(lst,' ',Chr(pgPtr^.page[loopByteCnt]),'  ');
  785.                 end
  786.             else
  787.                 begin
  788.                 Write(lst,' *  ');
  789.                 end;
  790.             end;
  791.         Writeln(lst); Writeln(lst);
  792.         if byteCnt + LINESIZE > PAGESIZE then
  793.             begin
  794.             done := TRUE;
  795.             end
  796.         else
  797.             begin
  798.             Inc(byteCnt,LINESIZE);
  799.             end;
  800.         end;
  801.     until done;
  802.     Writeln(lst); Writeln(lst);
  803.     end;                                     (* end of PrintPageInfo routine *)
  804.  
  805.  
  806. (* This routine will print the desired page from the page buffer.  lst is the
  807.    parameter which specifies which text device you want to use for output.
  808.    Normally, it will be the printer.  Be sure that the device is initialized
  809.    properly using Assign and Rewrite prior to calling this routine.          *)
  810.  
  811.  
  812. (* This routine will print the entire page buffer.  lst is the parameter which
  813.    specifies which text device you want to use for output. Normally, it will
  814.    be the printer.  Be sure that the device is initialized properly using
  815.    Assign and Rewrite prior to calling this routine.                         *)
  816.  
  817. procedure PrintPageBuffer(var lst : PrintTextDevice);
  818.  
  819. var
  820.     pgPtr : PagePtr;
  821.     cnt : PointerArrayRange;
  822.  
  823.     begin
  824.     SetCompressedMode(lst);            (* sets printer to 132 character mode *)
  825.     for cnt := 0 to POINTERARRAYSIZE do
  826.         begin
  827.         pgPtr := PointerArray[cnt];
  828.         while pgPtr <> NIL do
  829.             begin
  830.             PrintPageInfo(lst,pgPtr);
  831.             pgPtr := pgPtr^.nextPage;
  832.             end;
  833.         end;
  834.     CancelCompressedMode(lst);
  835.     end;                                   (* end of PrintPageBuffer routine *)
  836.  
  837.  
  838. procedure PrintPageBufferPage(var lst : PrintTextDevice;
  839.                               prNum : PrNumber);
  840.  
  841. var
  842.     pgPtr : PagePtr;
  843.     cnt : PointerArrayRange;
  844.  
  845.     begin
  846.     SetCompressedMode(lst);            (* sets printer to 132 character mode *)
  847.     for cnt := 0 to POINTERARRAYSIZE do
  848.         begin
  849.         pgPtr := PointerArray[cnt];
  850.         while pgPtr <> NIL do
  851.             begin
  852.             if pgPtr^.prNum = prNum then
  853.                 begin
  854.                 PrintPageInfo(lst,pgPtr);
  855.                 end;
  856.             pgPtr := pgPtr^.nextPage;
  857.             end;
  858.         end;
  859.     CancelCompressedMode(lst);
  860.     end;                                   (* end of PrintPageBuffer routine *)
  861.  
  862.  
  863. (* This routine will initialize the variables used to keep track of buffer
  864.    use statistics.                                                           *)
  865.  
  866. procedure InitializeBufferStats;
  867.  
  868.     begin
  869.     bufferAttempts := 0;
  870.     bufferHits := 0;
  871.     end;                             (* end of InitializeBufferStats routine *)
  872.  
  873.  
  874. (* This routine will return buffer statistics.  The statistic will be returned
  875.    in a a record of type BufferStats.                                        *)
  876.  
  877. procedure CreateBufferStats(var stats : BufferStats);
  878.  
  879.     begin
  880.     stats.pagesInUse := pagesInUse;
  881.     stats.maxPages := maxBufferPages;
  882.     stats.attempts := bufferAttempts;
  883.     stats.hits := bufferHits;
  884.     end;                                 (* end of CreateBufferStats routine *)
  885.  
  886. (*\*)
  887. (* This routine will print the buffer statistics.  lst is the parameter which
  888.    specifies which text device you want to use for output. Normally, it will
  889.    be the printer.  Be sure that the device is initialized properly using
  890.    Assign and Rewrite prior to calling this routine.                         *)
  891.  
  892. procedure PrintBufferStats(var lst : PrintTextDevice);
  893.  
  894. var
  895.     stats : BufferStats;
  896.  
  897.     begin
  898.     CreateBufferStats(stats);
  899.     Writeln(lst);
  900.     Writeln(lst,'** Buffer Statistics Follow: **');
  901.     Writeln(lst);
  902.     Writeln(lst,'Buffer Pages In Use = ',stats.pagesInUse);
  903.     Writeln(lst,'Maximum buffer pages available =  ',stats.maxPages);
  904.     Writeln(lst,'Attempts to Fetch Data = ',stats.attempts);
  905.     Writeln(lst,'Number of Hits = ',stats.hits);
  906.     if stats.attempts <> 0 then
  907.         begin
  908.         Writeln(lst,'Hit percentage = ',
  909.                 Trunc((stats.hits/stats.attempts)*100),'%');
  910.         end;
  911.     end;                                       (* end of PrintBuffer routine *)
  912.  
  913.  
  914.  
  915. begin
  916. New(reservedPgPtr);              (* reserve space for one page in the buffer *)
  917. InitializePointerArray;
  918. InitializeBufferStats;
  919. SetMaxBufferPages(256);                           (* initially a 128K buffer *)
  920. end.                                                     (* end of Page unit *)
  921.